package com.ushine.web.security.jwt;


import com.ushine.web.component.exception.LoginErrorEnum;
import com.ushine.web.security.response.SecurityResponse;
import com.ushine.web.security.singlelogin.CompareKickOutFilter;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.stereotype.Component;
import org.springframework.web.filter.OncePerRequestFilter;

import javax.annotation.Resource;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

/**
 * @Author: yls
 * @Date: 2021/8/19 13:38
 * @Description: 验证用户登录信息的拦截器
 * @Version 1.0
 */
@Slf4j
@Component
public class JwtAuthenticationFilter extends OncePerRequestFilter {

    @Resource
    private UserDetailsService userDetailsService;

    @Resource
    private JwtTokenUtil jwtTokenUtil;


    @Resource
    JwtProperties jwtProperties;

    @Resource
    SecurityResponse securityResponse;

    @Resource
    CompareKickOutFilter compareKickOutFilter;


    @SneakyThrows
    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
        String header = jwtProperties.getHeader();
        String token = request.getHeader(header);
        log.info("JwtAuthenticationFilter=》" + "请求：" + request.getRequestURI() + ",token=》" + token);
        // 规定前缀
        String prefix = jwtProperties.getPrefix();
        if (StringUtils.isBlank(prefix)) {
            // 没有前缀指定，不作任何操作
        } else if (StringUtils.isNotEmpty(token) && token.startsWith(prefix)) {
            token = token.substring(jwtProperties.getPrefix().length());
        } else {
            // 不按规范,不允许通过验证
            token = null;

        }
        if (StringUtils.isNotEmpty(token)) {
            /*
             * 判断token是否过期
             */
            Boolean tokenExpired = jwtTokenUtil.isTokenExpired(token);
            if (Boolean.TRUE.equals(tokenExpired)) {
                log.info("{}的登录失效，请重新登录。", token);
                securityResponse.getResponse(response, LoginErrorEnum.USER_ACCOUNT_EXPIRED.getErrorCode(), LoginErrorEnum.USER_ACCOUNT_EXPIRED.getErrorMsg(), "");
                return;
            }

            // 获取用户信息
            String username = jwtTokenUtil.getUsernameFromToken(token);
            /*
             * 判断异地登录
             */
            boolean accessAllowed = compareKickOutFilter.isAccessAllowed(username, token);
            if (Boolean.FALSE.equals(accessAllowed)) {
                securityResponse.getResponse(response, LoginErrorEnum.USER_ACCOUNT_USE_BY_OTHER_LOGIN.getErrorCode(),
                        LoginErrorEnum.USER_ACCOUNT_USE_BY_OTHER_LOGIN.getErrorMsg(), "");
                return;
            }

            // 如果可以正确的从jwt中提取用户信息，并且该用户未被授权
            if (username != null && SecurityContextHolder.getContext().getAuthentication() == null) {
                UserDetails userDetails = userDetailsService.loadUserByUsername(username);
                log.info("加载用户数据：" + userDetails);
                Boolean validateToken = jwtTokenUtil.validateToken(token, userDetails);
                if (Boolean.TRUE.equals(validateToken)) {
                    // 给使用该【jwt】令牌的用户进行授权
                    UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities());
                    // 交给【Spring Security】管理，在之后的过滤器中农不会再被拦截进行二次授权了
                    SecurityContextHolder.getContext().setAuthentication(authenticationToken);
                }
            }
        }
        filterChain.doFilter(request, response);
    }
}
